﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Microsoft.Xna.Framework
{
    public static class MatrixHelper
    {
        /// <summary>
        /// 根据三个点来计算缩放矩阵（X，Y不对称缩放矩阵）,最大缩放倍数为1.1和0.9,这样可以实现缩放的平滑过度。
        /// 缩放针对的是两个手指的缩放
        /// </summary>
        /// <param name="refPoint">参考点</param>
        /// <param name="oldPoint"></param>
        /// <param name="newPoint"></param>
        /// <returns></returns>
        public static Matrix ComputeScaleMatrix(Vector2 refPoint, Vector2 oldPoint, Vector2 newPoint)
        {
            float scaleX = (newPoint.X - refPoint.X) / (oldPoint.X - refPoint.X);
            float scaleY = (newPoint.Y - refPoint.Y) / (oldPoint.Y - refPoint.Y);

            if (float.IsNaN(scaleX) || float.IsInfinity(scaleX) ||
                float.IsNaN(scaleY) || float.IsInfinity(scaleY) ||
                scaleX <= 0 || scaleY <= 0)
            {
                return Matrix.Identity;//返回一个什么事都不做的矩阵对象，也就是对角单元格都为1
            }

            scaleX = Math.Min(1.1f, Math.Max(0.9f, scaleX));//最大缩放倍数为1.1和0.9
            scaleY = Math.Min(1.1f, Math.Max(0.9f, scaleY));//最大缩放倍数为1.1和0.9

            Matrix matrix = Matrix.CreateTranslation(-refPoint.X, -refPoint.Y, 0);//计算缩放和旋转的时候要先将中心点平移到原点
            matrix *= Matrix.CreateScale(scaleX, scaleY, 1);
            //计算完缩放和旋转后将中心点平移到参考点
            matrix.M41 += refPoint.X;
            matrix.M42 += refPoint.Y;
            return matrix;
        }
        /// <summary>
        /// 根据三个点计算旋转和平移操作
        /// 旋转和平移都是针对一个手指
        /// </summary>
        /// <param name="refPoint"></param>
        /// <param name="oldPoint"></param>
        /// <param name="newPoint"></param>
        /// <returns></returns>
        public static Matrix ComputeRotateAndTranslateMatrix(Vector2 refPoint, Vector2 oldPoint, Vector2 newPoint)
        {
            Matrix matrix = Matrix.Identity;
            Vector2 delta = newPoint - oldPoint;
            Vector2 oldVector = oldPoint - refPoint;
            Vector2 newVector = newPoint - refPoint;
            float newVector_Len = newVector.Length();
            float oldVector_Len = oldVector.Length();
            // 避免手指离中心点太近时的旋转
            if (newVector_Len > 25 && oldVector_Len > 25)
            {
                //计算从位图中心点到触控点的角度
                float oldAngle = (float)Math.Atan2(oldVector.Y, oldVector.X);
                float newAngle = (float)Math.Atan2(newVector.Y, newVector.X);

                //计算旋转矩阵
                float angle = newAngle - oldAngle;
               //计算缩放和旋转的时候要先将中心点平移到原点
                matrix.M41 -= refPoint.X;
                matrix.M42 -= refPoint.Y;
                matrix *= Matrix.CreateRotationZ(angle);
                //计算完缩放和旋转后将中心点平移到参考点
                matrix.M41 += refPoint.X;
                matrix.M42 += refPoint.Y;

                // 实际是旋转旧的向量
                oldVector = oldVector_Len / newVector_Len * newVector;

                //重新计算偏移量
                delta = newVector - oldVector;
            }
            //包含平移
            matrix *= Matrix.CreateTranslation(delta.X, delta.Y, 0);
            return matrix;
        }
        /// <summary>
        /// 根据三个点计算缩放（X,Y对称缩放）和旋转矩阵。
        /// 缩放针对两个手指，
        /// 旋转也时针对两个手指的旋转
        /// </summary>
        /// <param name="refPoint"></param>
        /// <param name="oldPoint"></param>
        /// <param name="newPoint"></param>
        /// <returns></returns>
        public static Matrix ComputeScaleAndRotateMatrix(Vector2 refPoint, Vector2 oldPoint, Vector2 newPoint)
        {
            Matrix matrix = Matrix.Identity;
            Vector2 oldVector = oldPoint - refPoint;
            Vector2 newVector = newPoint - refPoint;

            // 计算从引用点到触控点的角度
            float oldAngle = (float)Math.Atan2(oldVector.Y, oldVector.X);
            float newAngle = (float)Math.Atan2(newVector.Y, newVector.X);

            // 计算旋转矩阵
            float angle = newAngle - oldAngle;
            matrix *= Matrix.CreateTranslation(-refPoint.X, -refPoint.Y, 0);
            matrix *= Matrix.CreateRotationZ(angle);
            matrix *= Matrix.CreateTranslation(refPoint.X, refPoint.Y, 0);

            //实际是旋转旧的向量
            oldVector = oldVector.Length() / newVector.Length() * newVector;
            float scale = 1;

            // 从大的偏移量来计算缩放因子
            if (Math.Abs(newVector.X - oldVector.X) > Math.Abs(newVector.Y - oldVector.Y))
                scale = newVector.X / oldVector.X;
            else
                scale = newVector.Y / oldVector.Y;

            // 计算缩放矩阵
            if (!float.IsNaN(scale) && !float.IsInfinity(scale) && scale > 0)
            {
                scale = Math.Min(1.1f, Math.Max(0.9f, scale));//最大缩放倍数为1.1和0.9

                //计算缩放和旋转的时候要先将中心点平移到原点
                matrix.M41 -= refPoint.X;
                matrix.M42 -= refPoint.Y;
                matrix *= Matrix.CreateScale(scale, scale, 1);
                //计算完缩放和旋转后将中心点平移到参考点
                matrix.M41 += refPoint.X;
                matrix.M42 += refPoint.Y;
            }
            return matrix;
        }

        /// <summary>
        /// 返回一个1*1像素的放射变换矩阵
        /// </summary>
        /// <param name="ptUL">左上点</param>
        /// <param name="ptUR">右上点</param>
        /// <param name="ptDL">左下点</param>
        /// <returns></returns>
        static Matrix ComputeAffineTransform(Vector2 ptUL, Vector2 ptUR, Vector2 ptDL)
        {
            return new Matrix()
            {
                M11 = (ptUR.X - ptUL.X),
                M12 = (ptUR.Y - ptUL.Y),
                M21 = (ptDL.X - ptUL.X),
                M22 = (ptDL.Y - ptUL.Y),
                M33 = 1,
                M41 = ptUL.X,
                M42 = ptUL.Y,
                M44 = 1
            };
        }
        /// <summary>
        /// 通过四个点（左上，右上，左下，右下）来对某个对象进行非仿射变换。变换后，三个点永远保持在对象的四个角（左上，右上，左下，右下）上
        /// </summary>
        /// <param name="size">对象尺寸</param>
        /// <param name="ptUL"></param>
        /// <param name="ptUR"></param>
        /// <param name="ptDL"></param>
        /// <param name="ptDR"></param>
        /// <returns></returns>
        public static Matrix ComputeNonAffineTransform(Vector2 size, Vector2 ptUL, Vector2 ptUR,
                                                        Vector2 ptDL, Vector2 ptDR)
        {
            // 缩放变换
            Matrix S = Matrix.CreateScale(1 / size.X, 1 / size.Y, 1);//将原始尺寸缩小到1*1像素的尺寸

            // 放射变换
            Matrix A = ComputeAffineTransform(ptUL, ptUR, ptDL);

            // 非仿射变换
            Matrix B = new Matrix();
            float den = A.M11 * A.M22 - A.M12 * A.M21;
            float a = (A.M22 * ptDR.X - A.M21 * ptDR.Y +
                       A.M21 * A.M42 - A.M22 * A.M41) / den;

            float b = (A.M11 * ptDR.Y - A.M12 * ptDR.X +
                       A.M12 * A.M41 - A.M11 * A.M42) / den;

            B.M11 = a / (a + b - 1);
            B.M22 = b / (a + b - 1);
            B.M33 = 1;
            B.M14 = B.M11 - 1;
            B.M24 = B.M22 - 1;
            B.M44 = 1;

            // 三个变换的乘积
            return S * B * A;
        }
        /// <summary>
        /// 通过三个点（左上，右上，左下）来对某个对象进行仿射变换。变换后，三个点永远保持在对象的三个角（左上，右上，左下）上
        /// </summary>
        /// <param name="size">对象尺寸</param>
        /// <param name="ptUL"></param>
        /// <param name="ptUR"></param>
        /// <param name="ptDL"></param>
        /// <returns></returns>
        public static Matrix ComputeAffineTransform(Vector2 size, Vector2 ptUL, Vector2 ptUR, Vector2 ptDL)
        {
            // 缩放变换
            Matrix S = Matrix.CreateScale(1 / size.X, 1 / size.Y, 1);//将原始尺寸缩小到1*1像素的尺寸

            // 放射变换
            Matrix A = ComputeAffineTransform(ptUL, ptUR, ptDL);

            //两个变换的乘积
            return S * A;
        }

    }
}
